<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title> from 171101 to </title>
    <style>
        .state-body{
            /*设成inline-block，横向可伸缩*/
            display: inline-block;
            background: deepskyblue;
            position: relative;
        }
        .state-tip{
            width: 5px;
            height: 5px;
            background: red;
            position: absolute;
            top: 0px;
            right: 0px;
        }
        

    </style>
</head>
<body>

    <!-- 1.状态小标签-->
    <div class="state-body">
        可以伸缩的文字
        <div class="state-tip"></div>
    </div>
    


<script>
    /*
        1.slice()
        截取的起点，截取的终点（不包含）
    */
    ;
    (function () {
        var arr1=[1,2,3,4,5,6]
        var result1=arr1.slice(-3,-1)
//        console.log(arr1)     //不变
//        console.log(result1)    //4,5

        var arr2=[1,2,3,4,5,6]
        var result2=arr2.slice(1,3)
//        console.log(arr2)
//        console.log(result2)    //2,3
    })()



        /* 2.split() */
    ;
    (function () {
        var str='hello,my name is huo'
        var result=str.split(',')
//        console.log('str:', str)
//        console.log('result:',result)   // ['hello','my name is huo']
//        console.log('result[0]:',result[0])     // hello
    })()


        /* 3.window.location.href */
    ;
    (function () {
//        window.location.href='http://www.baidu.com'
    })()


        /* 4.encodeURIComponent() */
    ;
    (function () {
        var str='小强'
        var result=encodeURIComponent(str)
//        console.log('encode:', result)
        result=decodeURIComponent(result)
//        console.log('decode', result)
    })()


        /* 5.reduce */
    ;
    (function () {
        var arr=[1,2,3,4,5]
        var result=arr.reduce(function(pre,item){
            return pre + (item>2?1:0)
        }, 0)
//        console.log('arr:', arr)    //不变
//        console.log('result:', result)  //3
    })()
    

        /* 6.filter */
    ;
    (function () {
        var arr=[1,2,3,4,5]
        var result=arr.filter(function (item, index) {
            return item>2
        })
//        console.log('arr', arr)     //不变
//        console.log('result', result)   // [3, 4, 5]
    })()


        /* 7.map() */
    ;
    (function () {
        var arr=[1,2,3,4,5]
        var result=arr.map(function (item, index) {
            return item*10
        })
//        console.log('arr', arr)
//        console.log('result', result)   // [10, 20, 30, 40, 50]
    })()


        /* 8.获取url里的参数 */
    ;
    (function () {
        function getUrl(name){
            var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if(r!=null)return  unescape(r[2]); return null;
        }
        var result=getUrl('id')
//        console.log(result)
    })()


        /* 9.冒泡排序 */
    ;
    (function () {
        var arr=[1,3,5,3,7,8,2,4]
        function mySort(arr){
            var d
            for(var i= 0;i<arr.length;i++){
                for(var j=i;j<arr.length;j++){
                    if(arr[i]>arr[j]){
                        d=arr[i]
                        arr[i]=arr[j]
                        arr[j]=d
                    }
                }
            }
            return arr
        }
        var result=mySort(arr)
//        console.log('arr', arr)     //变化
//        console.log('result', result)   //排序
    })()


        /* 10.sort()排序 */
    ;
    (function () {
//        看起来挺不错的：
        var arr=[1,3,5,3,7,8,2,4]
        var result=arr.sort()
//        console.log('arr', arr)     //变化
//        console.log('result', result)   //从小到大
        var result=result.reverse()
//        console.log('result reverse()', result)     //相反

//        然而：
        var arr2=[100,12,34,200]
        var result2=arr2.sort()
//        console.log('result2', result2)     //[100,12,200,34]
//        因为默认情况下sort()方法是按照ASCII排序的

//        因此：
        var arr1=[100,12,34,200]
        var result1=arr1.sort(function(a, b){
            return a-b    //正序  [12, 34, 100, 200]
//            return b-a      //倒序
        })
//        console.log('result1', result1)
    })()


        /* 11.快速排序 */
    ;
    (function () {
        var arr=[100,12,34,200,31,55,99]
        function quickSort(arr){
            //如果数组<=1,则直接返回
            if(arr.length<=1){return arr;}
            var pivotIndex=Math.floor(arr.length/2);
            //找基准，并把基准从原数组删除
            var pivot=arr.splice(pivotIndex,1)[0];
            //定义左右数组
            var left=[];
            var right=[];

            //比基准小的放在left，比基准大的放在right
            for(var i=0;i<arr.length;i++){
                if(arr[i]<=pivot){
                    left.push(arr[i]);
                }
                else{
                    right.push(arr[i]);
                }
            }
            //递归
            return quickSort(left).concat([pivot],quickSort(right));
        }
        var result=quickSort(arr)
//        console.log('arr', arr)
//        console.log('result', result)
    })()


        /* 12.希尔排序 */
    ;
    (function () {
        var arr=[100,12,34,200,31,55,99]
        function shellSort(arr){
            var N=arr.length;
            var h=1;
            while(h<N/3){
                h=3*h+1;//设置间隔
            }
            while(h>=1){
                for(var i=h; i<N; i++){
                    for(j=i; j>=h && arr[j]<arr[j-h]; j-=h){
                        swap(arr, j, j-h);
                    }
                }
                h=(h-1)/3;
            }
        }
        function swap(array, i, j){//两个数调换
            var temp =array[j];
            array[j]=array[i];
            array[i]=temp;
        }
        var result=shellSort(arr)
//        console.log('arr:', arr)    //正序
//        console.log('result:', result)      //无返回值
    })()


        /* 数组方法大全： */
        /* 13.join() */
    ;
    (function () {
        var arr=[1,3,4]
        var result=arr.join()
        var result2=arr.join('-')
//        console.log('arr:', arr)              //[1,3,4]
//        console.log('result join()', result)    //1,3,4
//        console.log('result join("-")', result2)    //1-3-4

//        利用join创建一个可以复制字符串的函数
        var str='闪萌'
        function repeatStr(str){
            return new Array(20).join(str)
        }
        var result3=repeatStr(str)
//        console.log('repeatStr:', result3)    //19个‘闪萌’
    })()
    

        /* 14.push() pop() unshift() shift() */
    ;
    (function () {
        var arr1=[1,2,3]
        var arr2=[4,5,6]
        var result=arr1.push(arr2)
//        console.log('arr1', arr1)   // [1,2,3,Array(3)]
//        console.log('arr1.push(arr2) :', result)    // 返回数量

        var arr=[11,22,33]
        ;[].push.apply(arr,arr2)    // [11,22,33,4,5,6]
//        console.log('arr', arr)

    })()


        /* 15.concat() */
    ;
    (function () {
        var arr1=[1,2,3]
        var arr2=[4,5,6]
        var result=arr1.concat(arr2)
//        console.log('arr', arr1)
//        console.log('arr1.concat(arr2)', result)
//        console.log('arr1.concat([11,111][22,222])', arr1.concat([11,111],[22,222]))    // 1,2,3,11,111,22,222
    })()


        /* 16.splice() */
    ;
    (function () {
        var arr=[1,2,3,4,5,6]
        var result1=arr.splice(2,0,'hi')    // [1,2,'hi',3,4,5,6]
        arr.splice(2,1,'hello')     // [1,2,'hello',3,4,5,6]
        arr.splice(2,1)     // [1,2,3,4,5,6]
//        console.log('arr', arr)
//        console.log('arr.splice(2,0,"hi")',result1)     //splice()无返回值
        arr.splice(-3,2)
//        console.log('arr.splice(-3,2):', arr)     // [1,2,3,6]
    })()


        /* 17.indexOf()  lastIndexOf() */
    ;
    (function () {
        var arr=[1,2,3,4,2,4,3]
        var result=arr.indexOf(3)   //2
        var result3=arr.indexOf(3,2)    //2
        var result4=arr.indexOf(3,3)    //6 (找第一个3，从index为3的地方开始找)
        var result2=arr.lastIndexOf(3)  //6
        var result5=arr.lastIndexOf(3,6)    //6
        var result6=arr.lastIndexOf(3,5)    //2 （找最后一个3，截止到index为3的地方）
//        console.log(result6)
    })()
    

        /* 18.forEach() */
    ;
    (function () {
        var arr=[1,2,3,4,2,4,3]
        var result=arr.forEach(function (item, index) {
//            console.log(item, index)      //执行逻辑
        })
//        console.log('arr', arr)   //原数组不变
//        console.log('result', result)   //forEach()也没有返回值
    })()


        /* 19.every() some() */
    ;
    (function () {
        var arr=[1,2,3,4,2,4,3]
//        every(): 都符合条件返回true
        var result=arr.every(function (item, index) {
            return item>0
        })
//        console.log(result)

//        some(): 只要有一个符合条件就返回true
        var result2=arr.some(function (item, index){
            return item>3
        })
//        console.log(result2)
    })()


        /* 20.includes() */
    ;
    (function () {
        var arr=[1,2,3,4,2,4,3]
        var str='hello world'
        var result=arr.includes(2)
        var result2=str.includes('wor')
//        console.log(result);    // true
//        console.log(result2)    // true
    })()



        /* 21.移动端解决ios滑动卡顿 */
    ;
    (function () {
//        在滑动项？上添加样式：
//        -webkit-overflow-scrolling: touch;
    })()



        /* 22.parseInt() */
    ;
    (function () {
        var num = 111
        var res1 = parseInt(num)
//        console.log('res1:', res1)    //111
        var res2 = parseInt(num, 2)
//        第二个参数表示以几进制解析
//        console.log('res2', res2)     //7

        var numWithStr = '123.4abc'
        var res3 = parseInt(numWithStr)
//        console.log('res3:', res3)  //123
        var res4 = parseFloat(numWithStr)
//        console.log('res4:', res4)  //123.4
        var res5 = +numWithStr
//        console.log('res5:', res5)  //NaN
    })()



        /* 23.charAt toUpperCase replace */
    ;
    (function () {
        var str = '我爱北京天安门 i love beijing'
        var res1 = str.charAt(0)
//        console.log('res1', res1)
        var res2 = str.toUpperCase()
//        console.log('res2:', res2)
        var res3 = str.replace('我爱', '我想去')
//        console.log('res3:', res3)
    })()



        /* 24.switch */
    ;
    (function () {
        var num = '2'
//        case后面的和num的比较是===全等比较
        switch (num){
            case 1:
                console.log('1')
                break
            case 2:
                console.log('2')
//                break
            case 3:
                console.log('3')
                break
            default :
//                console.log('default')
        }
    })()



        /* 25.数组的长度 */
    ;
    (function () {
        var arr = ['item1', 'item2']
//        console.log('arr.length:', arr.length)  //2
        arr[99] = 'item3'
//        console.log('arr.length:', arr.length)  //100
//        console.log(typeof(arr[98]))
    })()



        /* 26.undefined运算 */
    ;
    (function () {
//        console.log(undefined+1)
//        console.log(undefined+'1')
//        console.log(NaN+1)
//        console.log(NaN+'1')
    })()



        /* 27.Symbol */
    ;
    (function () {
        var s = Symbol()
        // console.log('typeof s:', typeof s)
        var s1 = Symbol('haha')
//        console.log('typeof s1:', typeof s1)
        s1.toString()
//        console.log('s1:', s1)

        var a = Symbol('a')
        var b = Symbol('a')
        // console.log('a:', a)        //Symbol(a)
        // console.log('b:', b)        //Symbol(a)
        

//        console.log('==?', a==b)    //false
//        console.log('===?', a===b)  //false

        setTimeout(()=>{
//            console.log('es6')
        }, 3000)
    })()



        /* 28.for in 和 for of */
//    for in用于遍历对象(如果遍历数组，会遍历数组的所有可遍历属性，包括原型。所以for in最好结合hasOwnProperty使用)
//    for of可以用于遍历数组，遍历的值
//    foreach遍历的时候，既不能用break终止遍历，又不能用return跳到外层函数
    ;
    (function () {
        var obj = {
            name: '小强',
            age: 18
        }
        for (key in obj){
//            console.log('key:', key)
//            console.log('value:', obj[key]) //小强 18
        }

        var arr = [1, 2, 5, 9, 4]
        for(value of arr){
//            console.log('value:', value)       //1 2 5 9 4
        }



        arr.forEach(function(value){
            if(value == 5){
                return
            }
//            console.log(value)  // 1 2 9 4
//            这里的return没有使其跳到外层函数，而是跳出了本次的遍历，继续下一次的遍历
        })

        arr.filter(function (value) {
            if(value == 5){
                return
            }
//            console.log(value)  // 1 2 9 4
//            这里也是？
        })
    })()



        /* 29.parseInt parseFloat Number*/
    ;
    (function () {
//        1.Number不能转换整体上不是数字的东西（既不是数字字符串，也不是数字）
        var num1 = Number(3.14)
        var num2 = Number('3.14')
//        var num3 = Number(3.14abc)
        var num4 = Number('3.14abc')
//        console.log('num1:', num1)  //3.14
//        console.log('num2:', num2)  //3.14
//        console.log('num3:', num3)  //报错：Invalid or unexpected token
//        console.log('num4:', num4)  //NaN

//        console.log(10 == 010)  //false
//        在比较的过程中，以0开头的被转换成了八进制
//        然鹅，在ES5的严格模式下，这个特性被移除了

//        console.log(Number(0x10))   //16
//        console.log(Number(0b10))   //2
//        console.log(Number(010))        //8
//        console.log(Number('010'))      //10

//        2.parseInt只能解析出整数部分
        var num5 = parseInt('3.14abc')
        var num6 = parseFloat('3.14abc')
//        console.log('num5', num5)   //3
//        console.log('num6', num6)   //3.14

//        3.parseInt的第二个参数可以设置基数，按这个基数的进制来进行解析
        var num7 = parseInt('0x123')    //291
        var num8 = parseFloat('0x123')  //0
        var num9 = parseInt('0x123', 16)    //291
        var num10 = parseInt('0x123', 10)   //0

//        console.log('num7:', num7)
//        console.log('num8:', num8)
//        console.log('num9:', num9)
//        console.log('num10:', num10)

//        从num7可以看出，parseInt可以自动识别出进制，并相应解析；同时，又能通过第二个参数，改变解析时使用的进制

//        4.算数运算符
//        算数运算符的转化原理是Number()
        var a = '123'
        var b = '123a'
        var num11 = +a  //123
        var num12 = +b  //NaN
//        console.log('num11:', num11)
//        console.log('num12:', num12)
    })()



        /* 30.date */
    ;
    (function () {
        var d = new Date()
        var myDate = d.getDate()    //日期
        var myDay = d.getDay()      //星期---星期日是0
        var myMonth = d.getMonth()  //月份---好像比正常月份少1
        var myFullYear = d.getFullYear()    //年份
//        console.log(myDate,myDay,myMonth,myFullYear)
    })()



        /* 31.RegExp */
    ;
    (function () {
        var reg = /^abc$/
        var reg2 = /^abc|abc$/
//        console.log(reg.test('abc'))        //true
//        console.log(reg.test('abcabc'))     //false
//        虽然^表示以什么开头，$表示以什么结尾，但是如reg所示的正则并不表示满足前两个条件即可，而是表示要完全匹配
//        console.log(reg2.test('abcabc'))    //true
    })()



        /* 32.闭包 */
    ;
    (function () {

        for(var i=0;i<10;i++){
            setTimeout(function () {
//                console.log(i)  // 十个10
            }, 1000)
        }

        for(var i = 0; i < 10; i++) {
//            setTimeout(function(i) {
//                console.log(i);
//            }, 1000);

//            这里为什么在setTimeout中传入i会得到undefined?????????
        }


        for(var i=0;i<10;i++){
            (function(e){
                setTimeout(function () {
//                    console.log(e) // 0-9
                }, 1000)
            })(i)
        }
    })()



        /* 33.javascript所有变量都可以当对象使用，除了null和undefined */
    ;
    (function () {
//        console.log(null.toString())          //报错
//        console.log(undefined.toString());    //报错
//        console.log([1, 2, 3].toString());
//        console.log(false.toString());
//        var obj = {name: 'xiaoqiang'}
//        console.log(obj.toString());
//        console.log(123.toString());          //报错
//        一个常见的误解是数字的字面值（literal）不能当作对象使用。这是因为 JavaScript 解析器的一个错误， 它试图将点操作符解析为浮点数字面值的一部分。
//        console.log((123).toString());
    })()



        /* 34.删除属性的唯一方法是使用 delete 操作符；设置属性为 undefined 或者 null 并不能真正的删除属性， 而仅仅是移除了属性和值的关联。 */
    ;
    (function () {
        var obj = {
            name: 'xiaoqiang',
            gender: 'man',
            face: 'cool'
        }
        obj.name = null
        obj.gender = undefined
        delete obj.face
        for(var i in obj){
            if(obj.hasOwnProperty(i)){
//                console.log(i+':'+obj[i])
            }
        }
    })()



        /* 35.obj有没有length属性(没有)。Object.keys() */
    ;
    (function () {
        var obj = {
            name: '小强',
            age: '12'
        }
//        console.log(obj.length) //undefined

        var keysed = Object.keys(obj)
        var len = keysed.length
//        console.log(len)

    })()



        /* 36.将参数从一个函数传递到另一个函数的方法 */
    ;
    (function () {
        function foo(){
            bar.apply(null, arguments)
//            bar函数用foo函数的argu?
        }
        function bar(a, b, c){
//            干活
        }
    })()



        /* 37.构造函数的返回 */
    ;
    (function () {
//           通过new关键词调用的是构造函数
//           正常情况下返回的都是返回this对象（也就是新创建的对象）
//        特殊情况就是，被new调用的函数有显示的return且return的是一个对象

        function Test(){
            this.value = 2
            return {
                foo: 'hhh'
            }
        }

        var obj = new Test()
//        console.log(obj.value)  //undefined
//        console.log(obj.foo)    //'hhh'

//        但是new Test（）并不会将Test的pro totype指向返回的这个对象
//        而是指向new出来的对象（这个对象已经被new出来，只是没有被返回）

    })()

    

        /* 38.作用域与命名空间 */
    ;
    (function () {
        var items = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        for(var i = 0; i < 3; i++) {
            subLoop();
        }

        function subLoop() {
            // subLoop 函数作用域
            for(i = 0; i < 4; i++) { // 没有使用 var 声明变量
                // 干活
//                console.log(items[i])
            }
        }
        //1, 2, 3, 4
//        因为subLoop中的i没有var，所以是全局变量，所以只会进行一轮，而不是预期中的三轮

//        局部变量的声明方式：1.参数；2.var

        if(!aBianliang){
            var aBianliang = {}
//            console.log('ok')   //ok
        }
//      因为这里的“aBianliang”被var过

//        #2
        if(typeof bBianliang){
//            console.log(typeof bBianliang)         //undefined
//            console.log(typeof(typeof bBianliang))  //string
//            因为typeof的返回结果是string类型（无论测试结果是什么类型），所以经常用typeof做判断而且不会有报错的危险
        }
    })()



        /* 39.变量查找顺序 */
    ;
    (function () {
//        比如，当访问函数内的 foo 变量时，JavaScript 会按照下面顺序查找：
//        1.当前作用域内是否有 var foo 的定义。
//        2.函数形式参数（形参）是否有使用 foo 名称的。
//        3.函数自身是否叫做 foo。
//        4.回溯到上一级作用域，然后从 1 重新开始。
    })()



        /* 40.遍历时缓存数组的长度 */
    ;
    (function () {
        var list = [1, 2, 3, 4, 5];
        for(var i = 0, l = list.length; i < l; i++) {
//            console.log(list[i]);
        }
//        上面 l = list.length 就是缓存数组的长度，不用每次遍历都访问数组的长度，节省了性能
    })()



        /* 41.Array构造函数 */
    ;
    (function () {
//        1.创建数组的时候推荐使用字面量方式，因为new Array()的方式不好用
        var arr1 = new Array(1,2,3,4)   //传多个数字的时候，表示一个所见即所得的数组
        var arr2 = new Array(6)         //传一个数字的时候，表示一个长度为该数字的数组，而且还是空的（只是设置了长度）
//        console.log(arr1)   //[1,2,3,4]
//        console.log(arr2)   //[empty * 6]
        var arr3 = new Array('6')       //传一个的时候，传成字符串，“也不能”达到得到一个元素只有一个的数组的目的
//        console.log(arr3)       //["6"]

//        作用： 可以用来重复字符串
        var result = new Array(6).join('帅哥')
//        console.log(result)
    })()



        /* 42.检测一个对象的类型 */
    ;
    (function () {
        return

//        console.log(typeof [1,2,3])                 //"object"
//        console.log(typeof (new String('str')))     //"object"
//        用typeof大多数返回的是“object”，并不能起到检测类型的作用
//        唯一的作用： 测试未定义的变量(见#2)

//        检测一个对象的最好的方法：
//        Object.prototype.toString
        var obj={
            res1: Object.prototype.toString.call('hello'),
            res2: Object.prototype.toString.call(123),
            res3: Object.prototype.toString.call(false),
            res4: Object.prototype.toString.call(null),
            res5: Object.prototype.toString.call(undefined),
            res6: Object.prototype.toString.call({}),
            res7: Object.prototype.toString.call([]),
            res8: Object.prototype.toString.call(new Date())
        }

        for(var key in obj){
            console.log(key + ':' + obj[key])
        }

        console.log(typeof Object.prototype) // 'object'
        console.log(typeof Function.prototype) // 'function'

    //    为什么typeof Function.prototype是'function'。因为规定typeof [native code]就是function ?
    })()



        /* 43.Number()和new Number() */
    ;
    (function () {
//        console.log(Number(10) == new Number(10))   //true
//        console.log(Number(10) === new Number(10))  //false
//        console.log(Object.prototype.toString.call(Number(10)))         //[object Number]
//        console.log(Object.prototype.toString.call(new Number(10)))     //[object Number]
//        console.log(typeof Number(10))      //number
//        console.log(typeof new Number(10))  //object

//        由此可以看出，===的类型的比较应该是typeof的方式

//        console.log(typeof (new Number(10) + 0))    //number
    })()



        /* 44.为什么不使用eval */
    ;
    (function () {
//        绝对不要使用eval，所以也不要学习什么是eval，以免你记住了eval怎么用，但却忘记了不能用eval
    })()



        /* 45.转型函数String() */
    ;
    (function () {
//        转换规则：
//        1.如果有toString()方法，就调用该方法并返回结果。（数值、布尔值和字符串都有这个方法，它们可以变成包装器类）
//        2.如果值是null,返回“null”;(null和undefined都没有对应的包装器类，也就没有toString方法)
//        3.如果值是undefined，返回“undefined”;
    })()



        /* 46.constructor */
    ;
    (function () {
        function Test(){
            this.name = 'jinzhou'
        }
//        Test.prototype = {
//            age: 18
//        }
        Test.prototype.gender = 'man'
        var obj = new Test()
//        console.log(obj)

        var obj2 = { name: 'jinyu' }
//        console.log(obj2)

//      正常情况下，一个对象的prototype里保存着constructor属性，construcetor里是创建这个对象的函数
//      对于构造函数来说，就是再他为跟随者提供的公共空间prototype中，不但有一把通向Object的钥匙，
//      还有一把可以找到自己这个团队（函数是团队）的钥匙？
    })()




        /* 47.arguments与形参的同步 */
    ;
    (function () {
        function doAdd(num1, num2) {
            arguments[1] = 10;
            alert(arguments[0] + num2);
        }
//       每次执行函数的时候都会重写第二个参数num2，但是并不是访问了相同的内存空间，他们的内存空间是独立的（栈存储？），但是值会同步。

//        doAdd(3, 5)     //13
//        doAdd(3)        //NaN
//        如果只传入了一个参数,那么为 arguments[1]设置的值不会反应到命名参数中。
//        这是因为 arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名 参数的个数决定的
    })()



        /* 48.call() */
    ;
    (function () {
        function hello(thing) {
//            console.log(this + " says hello " + thing);
        }
        hello.call("xiaoqiang", "world") //=> Yehuda says hello world
    })()



        /* 49.test */
    ;
    (function () {
        return

        function doAdd(){
            var i = 1
            i++
//            console.log(i)
            function inDoAdd(){
                i++
                console.log(i)
            }
           inDoAdd()
           inDoAdd()
           inDoAdd()
        }
       doAdd()
       doAdd()
       doAdd()
    })()


        /* 50.一维数组和对象的深拷贝 */
    ;
    (function () {
//        一维>>>>>
//        数组： arr.slice()、arr.concat()、Array.from(arr)
//        对象： Object.assign({}, obj)

        var arr = [1,2,3,4]

//        var arr1 = arr.slice()
//        console.log(arr)
//        console.log(arr1)
//        arr1[0] = 9
//        console.log(arr)
//        console.log(arr1)

//        var arr2 = arr.concat()
//        console.log(arr)
//        console.log(arr2)
//        arr2[0] = 9
//        console.log(arr)
//        console.log(arr2)

//        var arr3 = Array.from(arr)
//        console.log(arr)
//        console.log(arr3)
//        arr3[0] = 9
//        console.log(arr)
//        console.log(arr3)

//        var obj = {name:'xiaoqiang', age: 18}
//        var obj1 = Object.assign({}, obj)
//        console.log(obj, obj1)
//        obj1.name = '小明'
//        console.log(obj, obj1)
    })()



        /* 51.多维深拷贝 */
    ;
    (function () {
//        JSON.parse(JSON.stringify(obj))
//        缺点：不能深拷贝含有undefined、function、symbol值的对象
//
        var obj = {
            x: {
                a: '小明',
                b: '小强'
            },
            y: undefined,
            z: function add(z1, z2) {
                return z1 + z2
            },
            a: Symbol("foo")
        }
        var result = JSON.parse(JSON.stringify(obj))
//        console.log('obj:', obj)
//        console.log('result:', result)
//        result.x.a = '吃吃吃吃'
//        console.log('obj:', obj)
//        console.log('result:', result)



//        复制的一个方法：
        function deepCopy(obj) {
            // 创建一个新对象
            let result = {}
            let keys = Object.keys(obj),
                    key = null,
                    temp = null;

            for (let i = 0; i < keys.length; i++) {
                key = keys[i];
                temp = obj[key];
                // 如果字段的值也是一个对象则递归操作
                if (temp && typeof temp === 'object') {
                    result[key] = deepCopy(temp);
                } else {
                    // 否则直接赋值给新对象
                    result[key] = temp;
                }
            }
            return result;
        }

        var obj1 = {
            x: {
                m: 1
            },
            y: undefined,
            z: function add(z1, z2) {
                return z1 + z2
            },
            a: Symbol("foo")
        };

        var obj2 = deepCopy(obj1);
        obj2.x.m = 2;

//        console.log(obj1); //{x: {m: 1}, y: undefined, z: ƒ, a: Symbol(foo)}
//        console.log(obj2); //{x: {m: 2}, y: undefined, z: ƒ, a: Symbol(foo)}

//        var arr1 = [1,2,3,[4,5,6]]
//        var arr2 = deepCopy(arr1)
//        arr2[3][0] = 999
//        console.log(arr1)
//        console.log(arr2)

//-------------------------------------------------------------------------------

    })()



        /* 52.封装console.log */
    ;
    (function () {
        function log(){
            var newArguments = []
            if(arguments.length > 0){
                for(var i = 0; i < arguments.length; i++){
                    newArguments.push('iphone',arguments[i])
                }
            }
            console.log.apply(console,newArguments)
        }
//        log('哈哈哈')

    })()


    /* 53.作用域和原型链 */
    ;
    (function () {
        return;     // 这个不用的时候要return出去，否则会影响其它小朋友！！！！

        // 先查找的作用域链， 然后查找的原型链？
        function foo() {
            var x = 10
            return function bar() {
                // var y = 20
                return function inner() {
                    // var y = 21
                    var z = 30
                    console.log('x', x, 'y', y, 'z', z)
                }
            }
        }
        Object.prototype.x = 111
        Object.prototype.y = 222
        Object.prototype.z = 333
        foo()()()

    })()


    /* 54.不用循环快速创建数组 */
    ;(function () {
        return
//稀疏数组
        var array = new Array(3);
        array[2] = "name";
        for(var a in array)
        {
            console.log("稀疏数组：" + "index=" + a + ",value=" + array[a]);
        }
// 密集数组
        var dense = Array.apply(null, Array(3));
        dense[2] = "name";
        for(var a in dense)
        {
            console.log("密集数组：" + "index=" + a + ",value=" + dense[a]);
        }

        var result = dense.map(item=>9)
        console.log('result:', result)

    })()


    /* 55.defineProperty */
    ;(function () {

        var obj = {
            name: '小强'
        }
        var objTemp = Object.assign({}, obj)

        Object.defineProperty(obj, 'name', {
            get: function() {
                return objTemp.name
            },
            set: function(newVal){
                objTemp.name = newVal
                onNameChange(newVal)
            }
        })

        function onNameChange(newVal) {
            console.log('name更新为'+newVal)
            console.log('obj:', obj)
        }

        setInterval(()=>{
            // obj.name += obj.name
        }, 2000)

    })()

    /* 56.组合继承 */
    ;(function () {
        function Person(name) {
            this.name = name
        }
        Person.prototype.sayName = function () {
            console.log('name', this.name)
        }

        function Student(name, grade) {
            // 1. 继承属性
            Person.call(this, name)
            this.grade = grade
        }
        // 2.继承方法
        Student.prototype = new Person()
        Student.prototype.sayGrade = function () {
            console.log('grade', this.grade)
        }

        var tom = new Student('汤姆', '六年级')
        // tom.sayName()
        // tom.sayGrade()

    // 寄生组合继承？？
    })()

    /* 57.arguments.callee */
    ;(function () {
        // 普通的递归
        function lesser(num) {
            if(num>0){
                num--
                console.log(num)
                lesser(num)
            }else{
                console.log('finished')
            }
        }
        // lesser(6)
        var lesser2 = lesser
        lesser = null
        // lesser2(6)      // 报错、因为递归中所使用的lesser已经被赋值为null

    //    不普通的递归：
        function goodLesser(num) {
            if(num>0){
                num--
                console.log(num)
                arguments.callee(num)   // arguments.callee指的就是本函数
            }else{
                console.log('finished')
            }
        }
        // goodLesser(6)
        var goodLesser2 = goodLesser
        goodLesser = null
        // goodLesser2(6)      // 正常

    //    但是在严格模式下会报错


    //    更好的实现，利用匿名函数：
        var bestLesser = (function f(num) {
            if(num>0){
                num--
                console.log(num)
                f(num)
            }else{
                console.log('finished')
            }
        })
        var bestLesser2 = bestLesser
        bestLesser = null
        // bestLesser2(6)      // 超级正常
    })()


    /* 58.全局变量和window的属性的区别 */
    // var globalDataA = 'i am global data a'
    // window.globalDataB = 'i am global data b'
    // delete  window.globalDataA  // false. 通过var声明的window的属性的configurable属性为false
    // delete  window.globalDataB  // true
    // if(window.aUndefinedDataA){
    //     console.log('dddddddddddd')
    // }
    // if(aUndefinedDataB){
    //     console.log('ssssssssss')
    // }

    /* 59.链式操作 */
    ;(function () {
        //    最简单的实现方法就是将当前的对象作为函数的返回值return出去。
        // 把当前对象return出去
        var CheckObject = function () {
            this.checkName = function () {
                console.log('name ok')
                return this
            }
            this.checkEmail = function () {
                console.log('email ok')
                return this
            }
            this.checkPassword = function () {
                console.log('password ok')
                return this
            }
        }
        var obj = new CheckObject()
        // obj.checkName().checkEmail().checkPassword()
    })()

    /* 60.闭包测试 */
    ;(function () {
        return

        //   验证：每次外部函数执行返回的内部函数的作用域读取的变量对象应该是不同的
        // （因为这个外部函数的变量对象是这个外部函数执行的时候创建的）
        function outter() {
            var count = 1
            return function () {
                count++
                console.log('count', count)
            }
        }
        var fun = outter()
        var foo = outter()
        fun()   // 2
        fun()   // 3
        foo()   // 2. 是独立于fun()的。说明两个函数中对应的那个变量对象是两个不同的对象
        fun()   // 4

        /*
        * 执行上下文：
        *
        *   1.变量对象（VO）；
        *   2.作用域链；
        *   3.this指向
        *
        * 作用域链又包括：
           （1） [[scope]]属性: 指向父级变量对象和作用域链，也就是包含了父级的[[scope]]和AO
           （2） AO: 自身活动对象
        * */



    })()


    /* 61. 一道作用域题 */
    ;(function () {

        return

        var d = 'Tom';
        function zeta() {
            if (d === undefined) {
                var d = 'Jerry';
            //    这里没有块级作用域，所以var d = 'Jerry'，变量提升导致zeta函数顶部已经有一个var d， d为undefined
            }
            console.log(d);
        }
        zeta();
    })()

    /* 62. Promise对象实现请求超时 */
    ;(function () {

        return

        var p1 = new Promise(function (resolve, reject) {
            var img = new Image()
            img.onload = function () {
                resolve(img)
            }
            img.src = 'xxxxx'
        })
        var p2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                reject('图片请求超时')
            }, 3000)
        })
        Promise.race([p1, p2])
            .then(function (res) {
                console.log('res', res)
            })
            .catch(function (reason) {
                console.log('reason', reason)
            })
    })()

</script>
</body>
</html>